package com.legendframework.core.dao.support;

import com.legendframework.core.dao.annotation.Column;
import com.legendframework.core.dao.annotation.TableId;
import com.legendframework.core.dao.exception.DaoException;
import sun.reflect.FieldAccessor;
import sun.reflect.ReflectionFactory;
import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl;
import sun.reflect.generics.reflectiveObjects.TypeVariableImpl;

import java.io.Serializable;
import java.lang.reflect.*;
import java.util.*;
import java.util.stream.Collectors;

public class ClassUtils {

    private static class ClassSuperNode{

        /**
         * 类
         */
        Class<?> cls;

        /**
         * 子类
         */
        ClassSuperNode classSuperNode;

        ClassSuperNode(Class<?> cls, ClassSuperNode classSuperNode) {
            this.cls = cls;
            this.classSuperNode = classSuperNode;
        }

        public Class<?> getCls() {
            return cls;
        }

        public void setCls(Class<?> cls) {
            this.cls = cls;
        }

        public ClassSuperNode getClassSuperNode() {
            return classSuperNode;
        }

        public void setClassSuperNode(ClassSuperNode classSuperNode) {
            this.classSuperNode = classSuperNode;
        }
    }

    /**
     * 校验这个类是否是基本类型
     * @param c
     * @return
     */
    public static boolean isScalar(Class c) {
        return c.isPrimitive() || c == String.class || c == Integer.class || c == Boolean.class || c == Float.class
                || c == Long.class || c == Double.class || c == Short.class || c == Byte.class || c == Character.class;
    }

    /**
     * 获取这个类的所有字段
     *
     * 包括所有父类的
     *
     * @param entityClass 类
     * @return 所有字段
     */
    public static List<Field> getByClass(Class<?> entityClass){
        return getByClass(entityClass,null);
    }

    /**
     * 获取这个类的所有字段
     *
     * 包括所有父类的,如果父类有泛型，会根据子类的实际类型替换
     *
     * @param entityClass 类
     * @return 所有字段
     */
    public static List<Field> getByClass(Class<?> entityClass , ClassSuperNode classSuperNode){
        //初始化节点
        if (classSuperNode == null) {
            classSuperNode = new ClassSuperNode(entityClass,null);
        }
        if (entityClass == null) {
            return new ArrayList<>(0);
        }
        List<Field> fields = new ArrayList<>(Arrays.asList(entityClass.getDeclaredFields()));
        if (classSuperNode.getClassSuperNode() != null) {
            //如果字段有泛型则获取泛型类型
            for (int i = 0; i < fields.size(); i++) {
                Field field = fields.get(i);
                if (field.getGenericType().getClass().equals(Class.class)) {
                    continue;
                }
                if (TypeVariableImpl.class.isAssignableFrom(field.getGenericType().getClass())) {
                    //获取泛型索引
                    int typeIndex = typeVarIndex((TypeVariableImpl) field.getGenericType());
                    //获取这个索引的实际类型，向上找
                    ClassSuperNode indexNode = classSuperNode;
                    Class cls = null;
                    while (indexNode.getClassSuperNode() != null) {
                        Type genericSuperclass = indexNode.getClassSuperNode().getCls().getGenericSuperclass();
                        Type type = ((ParameterizedTypeImpl) genericSuperclass).getActualTypeArguments()[typeIndex];
                        if (type.getClass().equals(Class.class)) {
                            cls = (Class) type;
                            break;
                        } else if (ParameterizedTypeImpl.class.isAssignableFrom(type.getClass())) {
                            cls = ((ParameterizedTypeImpl) type).getRawType();
                            break;
                        }
                        //新的索引
                        typeIndex = typeVarIndex(((TypeVariableImpl) type));
                        //继续向上找
                        indexNode = indexNode.getClassSuperNode();
                    }
                    if (cls == null) {
                        continue;
                    }
                    try {
                        Field root = Field.class.getDeclaredField("root");
                        root.setAccessible(true);
                        Object rootValue = root.get(field);
                        if (rootValue != null) {
                            root.set(field,null);
                        }
                        Method copy = Field.class.getDeclaredMethod("copy");
                        copy.setAccessible(true);
                        Field result = (Field) copy.invoke(field);
                        if (rootValue != null) {
                            root.set(field,rootValue);
                        }
                        Field type = Field.class.getDeclaredField("type");
                        type.setAccessible(true);
                        type.set(result,cls);
                        root.set(result,field);
                        //V1.0.4版本遇到的问题，需要设置fieldAccessor
                        Field reflectionFactory = AccessibleObject.class.getDeclaredField("reflectionFactory");
                        reflectionFactory.setAccessible(true);
                        ReflectionFactory ref = (ReflectionFactory) reflectionFactory.get(null);

                        //移除accessor
                        Method setFieldAccessor = Field.class.getDeclaredMethod("setFieldAccessor", FieldAccessor.class, boolean.class);
                        setFieldAccessor.setAccessible(true);
                        setFieldAccessor.invoke(result,null, false);
                        setFieldAccessor.invoke(result,null, true);

                        FieldAccessor fieldAccessor = ref.newFieldAccessor(result, result.isAccessible());
                        setFieldAccessor.setAccessible(true);
                        setFieldAccessor.invoke(result,fieldAccessor, result.isAccessible());
                        fields.set(i, result);
                    } catch (NoSuchMethodException | NoSuchFieldException | IllegalAccessException | InvocationTargetException e) {
                        e.printStackTrace();
                    }
                }

            }
        }
        if (Object.class.equals(entityClass.getSuperclass())){
            return fields;
        }
        List<Field> superFields = getByClass(entityClass.getSuperclass(),new ClassSuperNode(entityClass.getSuperclass(),classSuperNode));
        for (Field superField : superFields) {
            if (fields.stream().map(Field::getName).collect(Collectors.toList()).contains(superField.getName())){
                continue;
            }
            fields.add(superField);
        }
        return fields;
    }

    /**
     * 获取这个字段的泛型在类泛型的Index
     * @param typeVariable
     * @return
     */
    private static int typeVarIndex(TypeVariableImpl typeVariable) {
        TypeVariable[] var1 = typeVariable.getGenericDeclaration().getTypeParameters();
        int var2 = -1;
        TypeVariable[] var3 = var1;
        int var4 = var1.length;

        for(int var5 = 0; var5 < var4; ++var5) {
            TypeVariable var6 = var3[var5];
            ++var2;
            if (typeVariable.equals(var6)) {
                return var2;
            }
        }
        return -1;
    }

    /**
     * 获取这个类的所有方法
     *
     * 包括所有父类的
     *
     * @param entityClass 类
     * @return 所有字段
     */
    public static List<Method> getMethodsByClass(Class<?> entityClass){
        List<Method> methods = new ArrayList<>(Arrays.asList(entityClass.getDeclaredMethods()));
        if (Object.class.equals(entityClass.getSuperclass())){
            return methods;
        }
        List<Method> superMethods = getMethodsByClass(entityClass.getSuperclass());
        for (Method superMethod : superMethods) {
            if (methods.stream().map(Method::getName).collect(Collectors.toList()).contains(superMethod.getName()) &&
                    methods.stream().allMatch(method -> compare(method.getParameterTypes(), superMethod.getParameterTypes()))){
                continue;
            }
            methods.add(superMethod);
        }
        return methods;
    }

    /*
     * 定义方法比较两个数组内容是否相同
     * */
    public static boolean compare(Object[] arr1, Object[] arr2) {
        if (arr1.length != arr2.length) {
            return false;
        }
        for (int i = 0; i < arr1.length; i++) {
            if (arr1[i] != null && arr2[i] !=null) {
                if (!arr1[i].equals(arr2[i])) {
                    return false;
                }
            }
        }
        return  true;
    }

    /**
     * 获取该字段的get方法
     * @param entityClass 类
     * @param field 字段
     * @return
     */
    public static Method getGetterMethodByField(Class<?> entityClass , Field field){
        try {
            return entityClass.getMethod("get"+field.getName().substring(0,1).toUpperCase()+field.getName().substring(1));
        } catch (NoSuchMethodException e) {
            throw new DaoException("获取字段get方法时失败,字段名:"+field.getName());
        }
    }

    /**
     * 获取该字段的set方法
     * @param entityClass 类
     * @param field 字段
     * @return
     */
    public static Method getSetterMethodByField(Class<?> entityClass , Field field){
        try {
            return entityClass.getMethod("set"+field.getName().substring(0,1).toUpperCase()+field.getName().substring(1),field.getType());
        } catch (NoSuchMethodException e) {
            throw new DaoException("获取字段set方法时失败,字段名:"+field.getName());
        }
    }

    /**
     * 获取泛型对象的指定字段值的Value
     * @param t 实体类对象
     * @param fieldName 字段名称
     * @param <T>
     * @return
     */
    public static <T> Object getFieldValue(T t , String fieldName){
        Class<?> cls = t.getClass();
        Field field = null;
        List<Field> fields = getByClass(cls);
        for (Field declaredField : fields) {
            if (declaredField.isAnnotationPresent(Column.class)) {
                Column annotation = declaredField.getAnnotation(Column.class);
                if (annotation.exist() && annotation.value().equals(fieldName)) {
                    field = declaredField;
                    break;
                }
            }
        }
        if (field == null) {
            for (Field declaredField : fields) {
                if (declaredField.getName().equals(fieldName)) {
                    field = declaredField;
                    break;
                }
            }
        }
        if (field == null) {
            throw new DaoException(String.format("该对象没有这给字段: %s , %s",cls.getName(),fieldName));
        }
        try {
            return getGetterMethodByField(cls,field).invoke(t);
        } catch (Throwable e) {
            throw new DaoException(String.format("获取对象字段值失败: %s , %s",cls.getName(),fieldName));
        }
    }
    /**
     * 获取实体类中的ID
     * @param entity 实体类对象
     * @return
     */
    public static <T> Serializable getIdByEntity(T entity){
        Field idField = null;
        for (Field field : ClassUtils.getByClass(entity.getClass())) {
            if (field.isAnnotationPresent(TableId.class)) {
                idField = field;
                break;
            }
        }
        if (idField == null) {
            throw new DaoException("当前实体类没有ID字段: "+entity.getClass());
        }
        if (!Serializable.class.isAssignableFrom(idField.getType())) {
            throw new DaoException("实体类的ID字段必须实现序列化接口: "+entity.getClass());
        }
        idField.setAccessible(true);
        try {
            return (Serializable) idField.get(entity);
        } catch (IllegalAccessException e) {
            throw new DaoException("获取ID字段值失败: "+entity.getClass());
        }

    }

    /**
     * 获取一个类的所有接口，包括父类的接口
     * @param cls 类
     * @return
     */
    public static Class<?>[] getAllInterface(Class<?> cls){
        Set<Class<?>> classSet = Arrays.stream(cls.getInterfaces()).collect(Collectors.toSet());
        if (cls.getSuperclass() != null) {
            classSet.addAll(Arrays.stream(getAllInterface(cls.getSuperclass())).collect(Collectors.toSet()));
        }
        for (Class<?> aClass : cls.getInterfaces()) {
            classSet.addAll(Arrays.stream(getAllInterface(aClass)).collect(Collectors.toSet()));
        }
        return classSet.toArray(new Class<?>[0]);
    }

}
